<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf8">

  <meta name="description" content="WebGL 2.0 State Diagram to help visualize WebGL" />
  <meta name="keywords" content="webgl graphics" />
  <meta name="thumbnail" content="https://webgl2fundamentals.org/webgl/lessons/resources/webglfundamentals-webgl-state-diagram.jpg" />

  <meta property="og:title" content="WebGL2Fundamentals WebGL State Diagram" />
  <meta property="og:type" content="website" />
  <meta property="og:image" content="https://webglfundamentals.org/webgl/lessons/resources/webglfundamentals-webgl-state-diagram.jpg" />
  <meta property="og:description" content="WebGL State Diagram to help visualize WebGL" />
  <meta property="og:url" content="https://webgl2fundamentals.org/webgl/lessons/resources/webgl-state-diagram.html">

  <meta name="twitter:card" content="summary_large_image">
  <meta name="twitter:site" content="@greggman">
  <meta name="twitter:creator" content="@greggman">
  <meta name="twitter:domain" content="webgl2fundamentals.org">
  <meta name="twitter:title" content="WebGL2Fundamentals WebGL State Diagram">
  <meta name="twitter:url" content="https://webgl2fundamentals.org/webgl/lessons/resources/webgl-state-diagram.html">
  <meta name="twitter:description" content="WebGL State Diagram to help visualize WebGL" />
  <meta name="twitter:image:src" content="https://webgl2fundamentals.org/webgl/lessons/resources/webglfundamentals-webgl-state-diagram.jpg">

  <title>WebGLFundamentals WebGL State Diagram</title>

  <link href="/webgl/lessons/resources/webgl2fundamentals-icon.png" rel="shortcut icon" type="image/png">


  <!-- <link href="/3rdparty/monokai-sublime.css" rel="stylesheet"> -->
  <link href="/3rdparty/prism-material-dark.css" rel="stylesheet">
  <!-- <link href="/3rdparty/tomorrow-night-bright.css" rel="stylesheet"> -->
  <!-- <link href="/3rdparty/atom-one-dark-reasonable.css" rel="stylesheet">  --> 
  <link href="webgl-state-diagram/state-diagram.css" rel="stylesheet">
</head>
<body>
<div id="everything">
  <div id="diagram">
    <div id="canvas">
      <div class="name">canvas</div>
      <canvas></canvas>
    </div>
    <div id="global-state" class="window-content">
      <div class="name">global state</div>
    </div>

    <svg id="arrows" width="100%" height="100%">
      <defs>
      </defs>
    </svg>
  </div>
  <div id="codearea">
    <div id="ui">
      <div id="step">⇥</div>
      <div id="run">⇉</div>
      <div id="restart">↺</div>
      <div id="help">?</div>
      <div>
        <select id="example"></select>
      </div>
    </div>
    <div id="code"></div>
  </div>
</div>
<div class="get-webgl">
<pre>
((...))
( O O )  WebGL2 Cow 
 \   /   sees no WebGL2
 (`_`)
</pre>
</div>
<div id="hint"></div>

<template id="buffer-template">
<div class="webgl-object window-content buffer" data-help="
Buffers contain data provided to attributes.

Buffers are created with

```js
const buffer = gl.createBuffer();
```
">
  <div class="name">buffer</div>
  <div class="type"></div>
  <div class="content"></div>
</div>
</template>

<template id="renderbuffer-template">
<div class="webgl-object window-content renderbuffer" data-help="
Renderbuffers are an opaque kind of texture and can not usually
be read directly. Common examples are depth renderbuffers, stencil renderbuffers,
and in WebGL2 multi-sample renderbuffers for MSAA anti-aliasing.

Renderbuffers are created with

```js
const renderbuffer = gl.createRenderbuffer();
```
">
  <div class="name">renderbuffer</div>
  <div class="format"></div>
</div>
</template>

<template id="framebuffer-template">
<div class="webgl-object window-content framebuffer" data-help="
Framebuffers are an array of texture and renderbuffer attachments
used to bind them as a set as a render target, the place pixels
and/or depth values and/or stencil values will be written to and/or
read from.

Framebuffers are created with

```js
const framebuffer = gl.createFramebuffer();
```
">
  <div class="name">framebuffer</div>
  <div class="attachments"></div>
  <div class="state"></div>
</div>
</template>

<template id="texture-template">
<div class="webgl-object window-content texture" data-help="
Textures provide random access data to shaders. Most often
they contain image data but not always.

Textures are created with

```js
const tex = gl.createTexture();
```

and bound to a texture unit bind point (target) with

```
gl.activeTexture(gl.TEXTURE0 + texUnitIndex);
const bindPoint = gl.TEXTURE_2D;
gl.bindTexture(bindPoint, tex);
```

All texture functions reference textures
through the bind points on the active texture unit.
ie.

```js
texture = textureUnits[activeTexture][bindPoint]
```

For more details see [the article on textures](/webgl/lessons${langPathSegment}/webgl-3d-textures.html).
">
  <div class="name">texture</div>
  <div class="mips"></div>
</div>
</template>

<template id="sampler-template">
<div class="webgl-object window-content sampler" data-help="
Samplers let you override texture parameters.

Textures have several parameter set with `gl.texParameteri`
that choose how a texture is filtered, how it repeats,
and which mips are used.

To you the same texture with different settings you can
use a sampler. A sampler is assigned to a texture unit
making it possible to use the same texture with multiple
settings at the same time.

Samplers are created with

```js
const sampler = gl.createSampler();
```

and bound to a texture unit bind point (target) with

```js
gl.bindSampler(texUnitIndex, sampler);
```

They take all the same parameters as `gl.texParameteri`
except you pass the settings to `gl.samplerParameteri`
eg.

```js
gl.samplerParameteri(sampler, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
```
">
  <div class="name">sampler</div>
</div>
</template>

<template id="vertex-array-template">
  <div class="webgl-object window-content vertex-array" data-help="
Vertex Arrays contain all attribute state. Attributes define how
to pull data out of buffers to supply to a vertex shader.
You can create more vertex arrays with

```js
const someVertexArray = gl.createVertexArray();
```

and bind one (make it the current vertex array) with

```js
gl.bindVertexArray(someVertexArray);
```

Passing `null` to `gl.bindVertexArray` binds the default vertex array.
">
    <div class="name">vertex array</div>
    <div class="state-table">
    </div>
  </div>
</template>

<template id="transform-feedback-template">
  <div class="webgl-object window-content transform-feedback" data-help="
Transform Feedback objects contain the state needed to let a vertex shader
write its output to one or more buffers.

Imagine you have a vertex shader that declares 3 outputs

```glsl
out vec3 position;
out vec3 normal;
out vec4 color;
...
```

You have 2 options.

1. In `INTERLEAVED_ATTRIBS` mode you bind a single buffer to the first
attribute below (index 0). Each output is written consecutively to the
buffer. In other words if you output position(P), normal(N), color(C)
then PNCPNCPNCPNC... will be output to the single buffer

2. In `SEPARATE_ATTRIBS` mode you bind the buffers to the attributes
below. The first value output will be written to the first buffer,
the second to the second, etc.. so in the same example as above 
buffer0 would get PPPP..., buffer1 would get NNNN..., and buffer2
would get CCCC...

You bind buffers below with 

```js
gl.bindBufferBase(
    gl.TRANSFORM_FEEDBACK_BUFFER,
    indexOfAttribute,
    bufferToWriteTo);
```

or 

```js
const offset = 0;  // offset in bytes in buffer to start writing. Must be mult of 4
const size = 1000; // size of area of buffer to write to
gl.bindBufferRange(
    gl.TRANSFORM_FEEDBACK_BUFFER,
    indexOfAttribute,
    bufferToWriteTo,
    offset, 
    size);
```

You call `gl.transformFeedbackVaryings` **BEFORE LINKING A PROGRAM** to setup
which outputs are written to which attribute indices and which of the 2 modes to use.

Following the same example above of position, normal, color

```js
const mode = gl.SEPARATE_ATTRIBS; // or gl.INTERLEAVED_ATTRIBS
gl.transformFeedbackVaryings(someProgram, [
  'position',  // position is the first thing written
  'normal',    // normal is the second thing written
  'color',     // color is the third thing written
], mode);
```
">
    <div class="name">transform feedback</div>
    <div class="state-table"></div>
  </div>
</template>


<template id="vertex-shader-template">
  <div class="webgl-object window-content shader vertex-shader" data-help="
A vertex shader's sole responsibility is to set `gl_Position`
to a clip space position. To create one use:

```js
const shader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(shader, glslString);
gl.compileShader(shader);
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS) === 0) {
  console.error(gl.getShaderInfoLog(shader));
}
```
">
    <div class="name">shader</div>
  </div>
</template>

<template id="fragment-shader-template">
  <div class="webgl-object window-content shader fragment-shader" data-help="
A fragment shader's sole responsibility is declare an `out` color 
and set it to a color. To create a fragment shader use:

```js
const shader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(shader, glslString);
gl.compileShader(shader);
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS) === 0) {
  console.error(gl.getShaderInfoLog(shader));
}
```
">
    <div class="name">shader</div>
  </div>
</template>

<template id="program-template">
  <div class="webgl-object window-content program" data-help="
A program is a combination of a vertex shader
and a fragment shader *linked* together.

```js
const program = gl.createProgram();
gl.attachShader(program, someVertexShader);
gl.attachShader(program, someFragmentShader);
gl.linkProgram(program);
if (gl.getProgramParameter(program, gl.LINK_STATUS) === 0) {
  console.error(gl.getProgramInfoLog(program));
}
```
">
    <div class="name">program</div>
  </div>
</template>

</body>
<script id="docs-start" type="not-js">
# WebGL2 State diagram

This is an attempt to give a visual representation
of most of WebGL 2.0's internal state. 

WebGL 2.0 is really just an API to run shaders. The only
functions that actually write pixels are `gl.clear`,
`gl.drawArrays` and `gl.drawElements` and their variations. 
That's it!
All other API calls just setup internal state for when
those draw and clear functions are called.

## Instructions

* Click <span class="ui-button">⇥</span> to step one line
at a time and watch what WebGL state is affected.

* You might need to zoom out in your browser to see everything

* Anything you hover over that shows a 
  <span class="help-outline">red outline</span>
  can be clicked to show help.

* Choose different samples on the top right.

## Notes:

* A programs only indirectly
  reference textures via texture units by index. Similarly 
  programs only indirectly reference buffers via vertex
  array attribute index. The arrows showing
  these connection are dotted to indicate they are not
  direct connections.

  Program attributes are not connected to a specific vertex array
  nor are program uniforms connected to a particular texture.

* **Not all state is shown.**
</script>
<script id="draw-cubes" type="not-js">
'use strict';

const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl2');

// make a canvas with text in the center
const makeTextCanvas = (text, width, height, color) => {
  const ctx = document.createElement('canvas').getContext('2d')
  ctx.canvas.width = width
  ctx.canvas.height = height
  ctx.font = `bold ${height * 5 / 6 | 0}px sans-serif`
  ctx.textAlign = 'center'
  ctx.textBaseline = 'middle'
  ctx.fillStyle = color
  ctx.fillText(text, width / 2, height / 2)
  return ctx.canvas
};

const vsGLSL = `#version 300 es
in vec4 position;
in vec3 normal;
in vec2 texcoord;

uniform mat4 projection;
uniform mat4 modelView;

out vec3 v_normal;
out vec2 v_texcoord;

void main() {
    gl_Position = projection * modelView * position;
    v_normal = mat3(modelView) * normal;
    v_texcoord = texcoord;
}
`;

const fsGLSL = `#version 300 es
precision highp float;

in vec3 v_normal;
in vec2 v_texcoord;

uniform sampler2D diffuse;
uniform sampler2D decal;
uniform vec4 diffuseMult;
uniform vec3 lightDir;

out vec4 outColor;

void main() {
    vec3 normal = normalize(v_normal);
    float light = dot(normal, lightDir) * 0.5 + 0.5;
    vec4 color = texture(diffuse, v_texcoord) * diffuseMult;
    vec4 decalColor = texture(decal, v_texcoord);
    decalColor.rgb *= decalColor.a;
    color = color * (1.0 - decalColor.a) + decalColor; 
    outColor = vec4(color.rgb * light, color.a);
}
`;

const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vsGLSL);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
  throw new Error(gl.getShaderInfoLog(vertexShader))
};

const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fsGLSL);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
  throw new Error(gl.getShaderInfoLog(fragmentShader))
};

const prg = gl.createProgram();
gl.attachShader(prg, vertexShader);
gl.attachShader(prg, fragmentShader);
gl.linkProgram(prg);
if (!gl.getProgramParameter(prg, gl.LINK_STATUS)) {
  throw new Error(gl.getProgramInfoLog(prg))
};

// NOTE! These are only here to unclutter the diagram.
// It is safe to detach and delete shaders once
// a program is linked though it is arguably not common.
// and I usually don't do it.
gl.detachShader(prg, vertexShader);
gl.deleteShader(vertexShader);
gl.detachShader(prg, fragmentShader);
gl.deleteShader(fragmentShader);

const positionLoc = gl.getAttribLocation(prg, 'position');
const normalLoc = gl.getAttribLocation(prg, 'normal');
const texcoordLoc = gl.getAttribLocation(prg, 'texcoord');

const projectionLoc = gl.getUniformLocation(prg, 'projection');
const modelViewLoc = gl.getUniformLocation(prg, 'modelView');
const diffuseLoc = gl.getUniformLocation(prg, 'diffuse');
const decalLoc = gl.getUniformLocation(prg, 'decal');
const diffuseMultLoc = gl.getUniformLocation(prg, 'diffuseMult');
const lightDirLoc = gl.getUniformLocation(prg, 'lightDir');

// vertex positions for a cube
const cubeVertexPositions = new Float32Array([
    1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1,
]);
// vertex normals for a cube
const cubeVertexNormals = new Float32Array([
    1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1,
]);
// vertex texture coordinates for a cube
const cubeVertexTexcoords = new Float32Array([
    1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1,
]);
// vertex indices for the triangles of a cube
// the data above defines 24 vertices. We need to draw 12
// triangles, 2 for each size, each triangle needs
// 3 vertices so 12 * 3 = 36
const cubeVertexIndices = new Uint16Array([
    0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23,
],);

const cubeVertexArray = gl.createVertexArray();
gl.bindVertexArray(cubeVertexArray);

const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, cubeVertexPositions, gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(
    positionLoc,  // location
    3,            // size (components per iteration)
    gl.FLOAT,     // type of to get from buffer
    false,        // normalize
    0,            // stride (bytes to advance each iteration)
    0,            // offset (bytes from start of buffer)
);

const normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, cubeVertexNormals, gl.STATIC_DRAW);
gl.enableVertexAttribArray(normalLoc);
gl.vertexAttribPointer(
    normalLoc,  // location
    3,          // size (components per iteration)
    gl.FLOAT,   // type of to get from buffer
    false,      // normalize
    0,          // stride (bytes to advance each iteration)
    0,          // offset (bytes from start of buffer)
);

const texcoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, cubeVertexTexcoords, gl.STATIC_DRAW);
gl.enableVertexAttribArray(texcoordLoc);
gl.vertexAttribPointer(
    texcoordLoc,  // location
    2,            // size (components per iteration)
    gl.FLOAT,     // type of to get from buffer
    false,        // normalize
    0,            // stride (bytes to advance each iteration)
    0,            // offset (bytes from start of buffer)
);

const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndices, gl.STATIC_DRAW);

// This is not really needed but if we end up binding anything
// to ELEMENT_ARRAY_BUFFER, say we are generating indexed geometry
// we'll change cubeVertexArray's ELEMENT_ARRAY_BUFFER. By binding
// null here that won't happen.
gl.bindVertexArray(null);

const checkerTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, checkerTexture);
gl.texImage2D(
    gl.TEXTURE_2D,
    0,                // mip level
    gl.LUMINANCE,     // internal format
    4,                // width
    4,                // height
    0,                // border
    gl.LUMINANCE,     // format
    gl.UNSIGNED_BYTE, // type
    new Uint8Array([  // data
      192, 128, 192, 128,
      128, 192, 128, 192,
      192, 128, 192, 128,
      128, 192, 128, 192,
    ]));
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

const decalTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, decalTexture);
gl.texImage2D(
    gl.TEXTURE_2D,
    0,                // mip level
    gl.RGBA,          // internal format
    gl.RGBA,          // format
    gl.UNSIGNED_BYTE, // type
    makeTextCanvas('F', 32, 32, 'red'));
gl.generateMipmap(gl.TEXTURE_2D);

// above this line is initialization code
// --------------------------------------
// below is rendering code.

gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

gl.clearColor(0.5, 0.7, 1.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);

gl.useProgram(prg);

gl.bindVertexArray(cubeVertexArray);

// Picking unit 6 just to be different. The default of 0
// would render but would show less state changing.
let texUnit = 6;
gl.activeTexture(gl.TEXTURE0 + texUnit);
gl.bindTexture(gl.TEXTURE_2D, checkerTexture);
gl.uniform1i(diffuseLoc, texUnit);

texUnit = 3;
gl.activeTexture(gl.TEXTURE0 + texUnit);
gl.bindTexture(gl.TEXTURE_2D, decalTexture);
gl.uniform1i(decalLoc, texUnit);

gl.uniform3fv(lightDirLoc, m4.normalize([1, 5, 8]));

const projection = m4.perspective(
  60 * Math.PI / 180,  // fov
  gl.canvas.clientWidth / gl.canvas.clientHeight,  // aspect
  0.1,  // near
  10,   // far
);
gl.uniformMatrix4fv(projectionLoc, false, projection);

// draw center cube

let modelView = m4.identity();
modelView = m4.translate(modelView, 0, 0, -4);
modelView = m4.xRotate(modelView, 0.5);
modelView = m4.yRotate(modelView, 0.5);

gl.uniformMatrix4fv(modelViewLoc, false, modelView);

gl.uniform4fv(diffuseMultLoc, [0.7, 1, 0.7, 1]);

gl.drawElements(
    gl.TRIANGLES,
    36,                // num vertices to process
    gl.UNSIGNED_SHORT, // type of indices
    0,                 // offset on bytes to indices
);

// draw left cube

modelView = m4.identity();
modelView = m4.translate(modelView, -3, 0, -4);
modelView = m4.xRotate(modelView, 0.5);
modelView = m4.yRotate(modelView, 0.8);

gl.uniformMatrix4fv(modelViewLoc, false, modelView);

gl.uniform4fv(diffuseMultLoc, [1, 0.7, 0.7, 1]);

gl.drawElements(
    gl.TRIANGLES,
    36,                // num vertices to process
    gl.UNSIGNED_SHORT, // type of indices
    0,                 // offset on bytes to indices
);

// draw right cube

modelView = m4.identity();
modelView = m4.translate(modelView, 3, 0, -4);
modelView = m4.xRotate(modelView, 0.6);
modelView = m4.yRotate(modelView, -0.6);

gl.uniformMatrix4fv(modelViewLoc, false, modelView);

gl.uniform4fv(diffuseMultLoc, [0.7, 0.7, 1, 1]);

gl.drawElements(
    gl.TRIANGLES,
    36,                // num vertices to process
    gl.UNSIGNED_SHORT, // type of indices
    0,                 // offset on bytes to indices
);
</script>
<script id="samplers" type="not-js">
'use strict';

const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl2');

const vsGLSL = `#version 300 es
in vec4 position;
in vec3 normal;
in vec2 texcoord;

uniform mat4 projection;
uniform mat4 modelView;

out vec3 v_normal;
out vec2 v_texcoord;

void main() {
    gl_Position = projection * modelView * position;
    v_normal = mat3(modelView) * normal;
    v_texcoord = texcoord;
}
`;

const fsGLSL = `#version 300 es
precision highp float;

in vec3 v_normal;
in vec2 v_texcoord;

uniform sampler2D diffuse;
uniform vec4 diffuseMult;
uniform vec3 lightDir;

out vec4 outColor;

void main() {
    vec3 normal = normalize(v_normal);
    float light = dot(normal, lightDir) * 0.5 + 0.5;
    vec4 color = texture(diffuse, v_texcoord) * diffuseMult;
    outColor = vec4(color.rgb * light, color.a);
}
`;

const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vsGLSL);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
  throw new Error(gl.getShaderInfoLog(vertexShader))
};

const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fsGLSL);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
  throw new Error(gl.getShaderInfoLog(fragmentShader))
};

const prg = gl.createProgram();
gl.attachShader(prg, vertexShader);
gl.attachShader(prg, fragmentShader);
gl.linkProgram(prg);
if (!gl.getProgramParameter(prg, gl.LINK_STATUS)) {
  throw new Error(gl.getProgramInfoLog(prg))
};

// NOTE! These are only here to unclutter the diagram.
// It is safe to detach and delete shaders once
// a program is linked though it is arguably not common.
// and I usually don't do it.
gl.detachShader(prg, vertexShader);
gl.deleteShader(vertexShader);
gl.detachShader(prg, fragmentShader);
gl.deleteShader(fragmentShader);

const positionLoc = gl.getAttribLocation(prg, 'position');
const normalLoc = gl.getAttribLocation(prg, 'normal');
const texcoordLoc = gl.getAttribLocation(prg, 'texcoord');

const projectionLoc = gl.getUniformLocation(prg, 'projection');
const modelViewLoc = gl.getUniformLocation(prg, 'modelView');
const diffuseLoc = gl.getUniformLocation(prg, 'diffuse');
const diffuseMultLoc = gl.getUniformLocation(prg, 'diffuseMult');
const lightDirLoc = gl.getUniformLocation(prg, 'lightDir');

// vertex positions for a cube
const cubeVertexPositions = new Float32Array([
    1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1,
]);
// vertex normals for a cube
const cubeVertexNormals = new Float32Array([
    1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1,
]);
// vertex texture coordinates for a cube
const cubeVertexTexcoords = new Float32Array([
    1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1,
]);
// vertex indices for the triangles of a cube
// the data above defines 24 vertices. We need to draw 12
// triangles, 2 for each size, each triangle needs
// 3 vertices so 12 * 3 = 36
const cubeVertexIndices = new Uint16Array([
    0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23,
],);

const cubeVertexArray = gl.createVertexArray();
gl.bindVertexArray(cubeVertexArray);

const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, cubeVertexPositions, gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(
    positionLoc,  // location
    3,            // size (components per iteration)
    gl.FLOAT,     // type of to get from buffer
    false,        // normalize
    0,            // stride (bytes to advance each iteration)
    0,            // offset (bytes from start of buffer)
);

const normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, cubeVertexNormals, gl.STATIC_DRAW);
gl.enableVertexAttribArray(normalLoc);
gl.vertexAttribPointer(
    normalLoc,  // location
    3,          // size (components per iteration)
    gl.FLOAT,   // type of to get from buffer
    false,      // normalize
    0,          // stride (bytes to advance each iteration)
    0,          // offset (bytes from start of buffer)
);

const texcoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, cubeVertexTexcoords, gl.STATIC_DRAW);
gl.enableVertexAttribArray(texcoordLoc);
gl.vertexAttribPointer(
    texcoordLoc,  // location
    2,            // size (components per iteration)
    gl.FLOAT,     // type of to get from buffer
    false,        // normalize
    0,            // stride (bytes to advance each iteration)
    0,            // offset (bytes from start of buffer)
);

const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndices, gl.STATIC_DRAW);

// This is not really needed but if we end up binding anything
// to ELEMENT_ARRAY_BUFFER, say we are generating indexed geometry
// we'll change cubeVertexArray's ELEMENT_ARRAY_BUFFER. By binding
// null here that won't happen.
gl.bindVertexArray(null);

const checkerTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, checkerTexture);
gl.texImage2D(
    gl.TEXTURE_2D,
    0,                // mip level
    gl.LUMINANCE,     // internal format
    4,                // width
    4,                // height
    0,                // border
    gl.LUMINANCE,     // format
    gl.UNSIGNED_BYTE, // type
    new Uint8Array([  // data
      192, 128, 192, 128,
      128, 192, 128, 192,
      192, 128, 192, 128,
      128, 192, 128, 192,
    ]));


const linearSampler = gl.createSampler();
gl.samplerParameteri(linearSampler, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

const nearestSampler = gl.createSampler();
gl.samplerParameteri(nearestSampler, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.samplerParameteri(nearestSampler, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

// above this line is initialization code
// --------------------------------------
// below is rendering code.

gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

gl.clearColor(0.5, 0.7, 1.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);

gl.useProgram(prg);

gl.bindVertexArray(cubeVertexArray);

// Picking unit 6 just to be different. The default of 0
// would render but would show less state changing.
let texUnit = 6;
gl.activeTexture(gl.TEXTURE0 + texUnit);
gl.bindTexture(gl.TEXTURE_2D, checkerTexture);
gl.uniform1i(diffuseLoc, texUnit);

gl.uniform3fv(lightDirLoc, m4.normalize([1, 5, 8]));

const projection = m4.perspective(
  60 * Math.PI / 180,  // fov
  gl.canvas.clientWidth / gl.canvas.clientHeight,  // aspect
  0.1,  // near
  10,   // far
);
gl.uniformMatrix4fv(projectionLoc, false, projection);

// draw left cube

let modelView = m4.identity();
modelView = m4.translate(modelView, -2, 0, -4);
modelView = m4.xRotate(modelView, 0.5);
modelView = m4.yRotate(modelView, 0.5);

gl.uniformMatrix4fv(modelViewLoc, false, modelView);

gl.uniform4fv(diffuseMultLoc, [0.7, 1, 0.7, 1]);

gl.bindSampler(texUnit, linearSampler);

gl.drawElements(
    gl.TRIANGLES,
    36,                // num vertices to process
    gl.UNSIGNED_SHORT, // type of indices
    0,                 // offset on bytes to indices
);

// draw right cube

modelView = m4.identity();
modelView = m4.translate(modelView, 2, 0, -4);
modelView = m4.xRotate(modelView, 0.5);
modelView = m4.yRotate(modelView, 0.8);

gl.uniformMatrix4fv(modelViewLoc, false, modelView);

gl.uniform4fv(diffuseMultLoc, [1, 0.7, 0.7, 1]);

gl.bindSampler(texUnit, nearestSampler);

gl.drawElements(
    gl.TRIANGLES,
    36,                // num vertices to process
    gl.UNSIGNED_SHORT, // type of indices
    0,                 // offset on bytes to indices
);
</script>
<script id="smallest" type="not-js">
'use strict';

const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl2');

gl.clearColor(1, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
</script>
<script id="smallest-glsl" type="not-js">
'use strict';

const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl2');

const vsGLSL = `#version 300 es
void main() {
    gl_Position = vec4(0, 0, 0, 1);
    gl_PointSize = 100.0;
}
`;

const fsGLSL = `#version 300 es
precision highp float;

out vec4 outColor;

void main() {
    outColor = vec4(1, 0.5, 0, 1);
}
`;

const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vsGLSL);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
  throw new Error(gl.getShaderInfoLog(vertexShader))
};

const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fsGLSL);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
  throw new Error(gl.getShaderInfoLog(fragmentShader))
};

const prg = gl.createProgram();
gl.attachShader(prg, vertexShader);
gl.attachShader(prg, fragmentShader);
gl.linkProgram(prg);
if (!gl.getProgramParameter(prg, gl.LINK_STATUS)) {
  throw new Error(gl.getProgramInfoLog(prg))
};

gl.useProgram(prg);

// draw 1 point
gl.drawArrays(gl.POINTS, 0, 1);
</script>
<script id="triangle" type="not-js">
'use strict';

const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl2');

const vsGLSL = `#version 300 es
in vec4 position;
void main() {
    gl_Position = position;
}
`;

const fsGLSL = `#version 300 es
precision highp float;

out vec4 outColor;

void main() {
    outColor = vec4(0, 1, 0.5, 1);
}
`;

const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vsGLSL);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
  throw new Error(gl.getShaderInfoLog(vertexShader))
};

const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fsGLSL);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
  throw new Error(gl.getShaderInfoLog(fragmentShader))
};

const prg = gl.createProgram();
gl.attachShader(prg, vertexShader);
gl.attachShader(prg, fragmentShader);
gl.linkProgram(prg);
if (!gl.getProgramParameter(prg, gl.LINK_STATUS)) {
  throw new Error(gl.getProgramInfoLog(prg))
};


// NOTE! These are only here to unclutter the diagram.
// It is safe to detach and delete shaders once
// a program is linked though it is arguably not common.
// and I usually don't do it.
gl.detachShader(prg, vertexShader);
gl.deleteShader(vertexShader);
gl.detachShader(prg, fragmentShader);
gl.deleteShader(fragmentShader);

const positionLoc = gl.getAttribLocation(prg, 'position');

const triangleVAO = gl.createVertexArray();
gl.bindVertexArray(triangleVAO);

// in clip space
const vertexPositions = new Float32Array([
    0,   0.7,
  0.5,  -0.7,
 -0.5,  -0.7,
]);

const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertexPositions, gl.STATIC_DRAW);

gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(
    positionLoc,  
    2,            // 2 values per vertex shader iteration
    gl.FLOAT,     // data is 32bit floats
    false,        // don't normalize
    0,            // stride (0 = auto)
    0,            // offset into buffer
);

gl.useProgram(prg);

// compute 3 vertices for 1 triangle
gl.drawArrays(gl.TRIANGLES, 0, 3);
</script>
<script id="rainbow-triangle" type="not-js">
'use strict';

const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl2');

const vsGLSL = `#version 300 es
in vec4 position;
in vec4 color;

out vec4 v_color;

void main() {
    gl_Position = position;
    v_color = color;
}
`;

const fsGLSL = `#version 300 es
precision highp float;

in vec4 v_color;

out vec4 outColor;

void main() {
    outColor = v_color;
}
`;

const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vsGLSL);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
  throw new Error(gl.getShaderInfoLog(vertexShader))
};

const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fsGLSL);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
  throw new Error(gl.getShaderInfoLog(fragmentShader))
};

const prg = gl.createProgram();
gl.attachShader(prg, vertexShader);
gl.attachShader(prg, fragmentShader);
gl.linkProgram(prg);
if (!gl.getProgramParameter(prg, gl.LINK_STATUS)) {
  throw new Error(gl.getProgramInfoLog(prg))
};


// NOTE! These are only here to unclutter the diagram.
// It is safe to detach and delete shaders once
// a program is linked though it is arguably not common.
// and I usually don't do it.
gl.detachShader(prg, vertexShader);
gl.deleteShader(vertexShader);
gl.detachShader(prg, fragmentShader);
gl.deleteShader(fragmentShader);

const positionLoc = gl.getAttribLocation(prg, 'position');
const colorLoc = gl.getAttribLocation(prg, 'color');

const triangleVAO = gl.createVertexArray();
gl.bindVertexArray(triangleVAO);

// in clip space
const vertexPositions = new Float32Array([
    0,   0.7,
  0.5,  -0.7,
 -0.5,  -0.7,
]);

const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertexPositions, gl.STATIC_DRAW);

const vertexColors = new Uint8Array([
    255, 0, 0, 255,
    0, 255, 0, 255,
    0, 0, 255, 255,
]);

const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertexColors, gl.STATIC_DRAW);

gl.enableVertexAttribArray(positionLoc);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(
    positionLoc,  
    2,            // 2 values per vertex shader iteration
    gl.FLOAT,     // data is 32bit floats
    false,        // don't normalize
    0,            // stride (0 = auto)
    0,            // offset into buffer
);

gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.enableVertexAttribArray(colorLoc);
gl.vertexAttribPointer(
    colorLoc,  
    4,                // 4 values per vertex shader iteration
    gl.UNSIGNED_BYTE, // data is 8bit unsigned bytes
    true,             // do normalize
    0,                // stride (0 = auto)
    0,                // offset into buffer
);

gl.useProgram(prg);

// compute 3 vertices for 1 triangle
gl.drawArrays(gl.TRIANGLES, 0, 3);
</script>
<script id="draw-cube-on-cube" type="not-js">
'use strict';

const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl2');

const vsGLSL = `#version 300 es
in vec4 position;
in vec3 normal;
in vec2 texcoord;

uniform mat4 projection;
uniform mat4 modelView;

out vec3 v_normal;
out vec2 v_texcoord;

void main() {
    gl_Position = projection * modelView * position;
    v_normal = mat3(modelView) * normal;
    v_texcoord = texcoord;
}
`;

const fsGLSL = `#version 300 es
precision highp float;

in vec3 v_normal;
in vec2 v_texcoord;

uniform sampler2D diffuse;
uniform vec3 lightDir;

out vec4 outColor;

void main() {
    vec3 normal = normalize(v_normal);
    float light = dot(normal, lightDir) * 0.5 + 0.5;
    vec4 color = texture(diffuse, v_texcoord);
    outColor = vec4(color.rgb * light, color.a);
}
`;

const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vsGLSL);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
  throw new Error(gl.getShaderInfoLog(vertexShader))
};

const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fsGLSL);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
  throw new Error(gl.getShaderInfoLog(fragmentShader))
};

const prg = gl.createProgram();
gl.attachShader(prg, vertexShader);
gl.attachShader(prg, fragmentShader);
gl.linkProgram(prg);
if (!gl.getProgramParameter(prg, gl.LINK_STATUS)) {
  throw new Error(gl.getProgramInfoLog(prg))
};

// NOTE! These are only here to unclutter the diagram.
// It is safe to detach and delete shaders once
// a program is linked though it is arguably not common.
// and I usually don't do it.
gl.detachShader(prg, vertexShader);
gl.deleteShader(vertexShader);
gl.detachShader(prg, fragmentShader);
gl.deleteShader(fragmentShader);

const positionLoc = gl.getAttribLocation(prg, 'position');
const normalLoc = gl.getAttribLocation(prg, 'normal');
const texcoordLoc = gl.getAttribLocation(prg, 'texcoord');

const projectionLoc = gl.getUniformLocation(prg, 'projection');
const modelViewLoc = gl.getUniformLocation(prg, 'modelView');
const diffuseLoc = gl.getUniformLocation(prg, 'diffuse');
const lightDirLoc = gl.getUniformLocation(prg, 'lightDir');

// vertex positions for a cube
const cubeVertexPositions = new Float32Array([
    1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1,
]);
// vertex normals for a cube
const cubeVertexNormals = new Float32Array([
    1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1,
]);
// vertex texture coordinates for a cube
const cubeVertexTexcoords = new Float32Array([
    1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1,
]);
// vertex indices for the triangles of a cube
// the data above defines 24 vertices. We need to draw 12
// triangles, 2 for each size, each triangle needs
// 3 vertices so 12 * 3 = 36
const cubeVertexIndices = new Uint16Array([
    0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23,
],);

const cubeVertexArray = gl.createVertexArray();
gl.bindVertexArray(cubeVertexArray);

const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, cubeVertexPositions, gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(
    positionLoc,  // location
    3,            // size (components per iteration)
    gl.FLOAT,     // type of to get from buffer
    false,        // normalize
    0,            // stride (bytes to advance each iteration)
    0,            // offset (bytes from start of buffer)
);

const normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, cubeVertexNormals, gl.STATIC_DRAW);
gl.enableVertexAttribArray(normalLoc);
gl.vertexAttribPointer(
    normalLoc,  // location
    3,          // size (components per iteration)
    gl.FLOAT,   // type of to get from buffer
    false,      // normalize
    0,          // stride (bytes to advance each iteration)
    0,          // offset (bytes from start of buffer)
);

const texcoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, cubeVertexTexcoords, gl.STATIC_DRAW);
gl.enableVertexAttribArray(texcoordLoc);
gl.vertexAttribPointer(
    texcoordLoc,  // location
    2,            // size (components per iteration)
    gl.FLOAT,     // type of to get from buffer
    false,        // normalize
    0,            // stride (bytes to advance each iteration)
    0,            // offset (bytes from start of buffer)
);

const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndices, gl.STATIC_DRAW);


// This is not really needed but if we end up binding anything
// to ELEMENT_ARRAY_BUFFER, say we are generating indexed geometry
// we'll change cubeVertexArray's ELEMENT_ARRAY_BUFFER. By binding
// null here that won't happen.
gl.bindVertexArray(null);

const checkerTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, checkerTexture);
gl.texImage2D(
    gl.TEXTURE_2D,
    0,                // mip level
    gl.LUMINANCE,     // internal format
    4,                // width
    4,                // height
    0,                // border
    gl.LUMINANCE,     // format
    gl.UNSIGNED_BYTE, // type
    new Uint8Array([  // data
      192, 128, 192, 128,
      128, 192, 128, 192,
      192, 128, 192, 128,
      128, 192, 128, 192,
    ]));
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

const fbTextureWidth = 128;
const fbTextureHeight = 128;
const fbTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, fbTexture);
gl.texImage2D(
    gl.TEXTURE_2D,
    0,                // mip level
    gl.RGBA,          // internal format
    fbTextureWidth,   // width
    fbTextureHeight,  // height
    0,                // border
    gl.RGBA,          // format
    gl.UNSIGNED_BYTE, // type
    null,             // data
)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

const depthRB = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, depthRB);
gl.renderbufferStorage(
    gl.RENDERBUFFER,
    gl.DEPTH_COMPONENT16,  // format
    fbTextureWidth,        // width,
    fbTextureHeight,       // height,
);

const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, fbTexture, 0);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthRB);

// above this line is initialization code
// --------------------------------------
// below is rendering code.

// --------------------------------------
// First draw a cube to the texture attached to the framebuffer

gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.viewport(0, 0, fbTextureWidth, fbTextureHeight);

gl.clearColor(1, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);

gl.bindVertexArray(cubeVertexArray);

gl.useProgram(prg);

// Picking unit 6 just to be different. The default of 0
// would render but would show less state changing.
let texUnit = 6;
gl.activeTexture(gl.TEXTURE0 + texUnit);
gl.bindTexture(gl.TEXTURE_2D, checkerTexture);
gl.uniform1i(diffuseLoc, texUnit);

gl.uniform3fv(lightDirLoc, m4.normalize([1, 5, 8]));

// We need a perspective matrix that matches the
// aspect of the framebuffer texture
const projection = m4.perspective(
  60 * Math.PI / 180,                // fov
  fbTextureWidth / fbTextureHeight,  // aspect
  0.1,                               // near
  10,                                // far
);
gl.uniformMatrix4fv(projectionLoc, false, projection);

let modelView = m4.identity();
modelView = m4.translate(modelView, 0, 0, -4);
modelView = m4.xRotate(modelView, 0.5);
modelView = m4.yRotate(modelView, 0.5);

gl.uniformMatrix4fv(modelViewLoc, false, modelView);

gl.drawElements(
    gl.TRIANGLES,
    36,                // num vertices to process
    gl.UNSIGNED_SHORT, // type of indices
    0,                 // offset on bytes to indices
);

// --------------------------------------
// Now draw a cube to the canvas using
// the texture attached to the framebuffer

gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

gl.clearColor(0, 0, 1, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

// We need a perspective matrix that matches the
// aspect of the canvas
const projection = m4.perspective(
  60 * Math.PI / 180,  // fov
  gl.canvas.clientWidth / gl.canvas.clientHeight,  // aspect
  0.1,  // near
  10,   // far
);
gl.uniformMatrix4fv(projectionLoc, false, projection);

// Picking unit 3 just to be different. We could have
// stated with the same as above.
let texUnit = 6;
gl.activeTexture(gl.TEXTURE0 + texUnit);
gl.bindTexture(gl.TEXTURE_2D, fbTexture);
gl.uniform1i(diffuseLoc, texUnit);

// We didn't have to change attributes or other
// uniforms as we're just drawing the same cube vertices
// at the same place in front of the camera.

gl.drawElements(
    gl.TRIANGLES,
    36,                // num vertices to process
    gl.UNSIGNED_SHORT, // type of indices
    0,                 // offset on bytes to indices
);
</script>
<script id="transform-feedback" type="not-js">
'use strict';

const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl2');

const genPointsVSGLSL = `#version 300 es
uniform int numPoints;
out vec2 position;
out vec4 color;

#define PI radians(180.0)

void main() {
    float u = float(gl_VertexID) / float(numPoints);
    float a = u * PI * 2.0;
    position = vec2(cos(a), sin(a)) * 0.8;
    color = vec4(u, 0, 1.0 - u, 1);
}
`;

const genPointsFSGLSL = `#version 300 es
void main() {
  discard;
}
`;

const drawVSGLSL = `#version 300 es
in vec4 position;
in vec4 color;

out vec4 v_color;

void main() {
  gl_PointSize = 20.0;
  gl_Position = position;
  v_color = color;
}
`;

const drawFSGLSL = `#version 300 es
precision highp float;

in vec4 v_color;

out vec4 outColor;

void main() {
    outColor = v_color;
}
`;

const createShader = function(gl, type, glsl) {
  const shader = gl.createShader(type)
  gl.shaderSource(shader, glsl)
  gl.compileShader(shader)
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    throw new Error(gl.getShaderInfoLog(shader))
  }
  return shader
};

const createProgram = function(gl, vsGLSL, fsGLSL, outVaryings) {
  const vs = createShader(gl, gl.VERTEX_SHADER, vsGLSL)
  const fs = createShader(gl, gl.FRAGMENT_SHADER, fsGLSL)
  const prg = gl.createProgram()
  gl.attachShader(prg, vs)
  gl.attachShader(prg, fs)
  if (outVaryings) {
    gl.transformFeedbackVaryings(prg, outVaryings, gl.SEPARATE_ATTRIBS)
  }
  gl.linkProgram(prg)
  if (!gl.getProgramParameter(prg, gl.LINK_STATUS)) {
    throw new Error(gl.getProgramInfoLog(prg))
  }

  // NOTE! These are only here to unclutter the diagram.
  // It is safe to detach and delete shaders once
  // a program is linked though it is arguably not common.
  // and I usually don't do it.
  gl.detachShader(prg, vs)
  gl.deleteShader(vs)
  gl.detachShader(prg, fs)
  gl.deleteShader(fs)

  return prg
};

const genProg = createProgram(gl, genPointsVSGLSL, genPointsFSGLSL, ['position', 'color']);
const drawProg = createProgram(gl, drawVSGLSL, drawFSGLSL);

const numPointsLoc = gl.getUniformLocation(genProg, 'numPoints');

const posLoc = gl.getAttribLocation(drawProg, 'position');
const colorLoc = gl.getAttribLocation(drawProg, 'color');

const numPoints = 24;

// make a vertex array and attach 2 buffers
// one for 2D positions, 1 for colors.
const dotVertexArray = gl.createVertexArray();
gl.bindVertexArray(dotVertexArray);

const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, numPoints * 2 * 4, gl.DYNAMIC_DRAW);
gl.enableVertexAttribArray(posLoc);
gl.vertexAttribPointer(
    posLoc,       // location
    2,            // size (components per iteration)
    gl.FLOAT,     // type of to get from buffer
    false,        // normalize
    0,            // stride (bytes to advance each iteration)
    0,            // offset (bytes from start of buffer)
);

const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, numPoints * 4 * 4, gl.DYNAMIC_DRAW);
gl.enableVertexAttribArray(colorLoc);
gl.vertexAttribPointer(
    colorLoc,   // location
    4,          // size (components per iteration)
    gl.FLOAT,   // type of to get from buffer
    false,      // normalize
    0,          // stride (bytes to advance each iteration)
    0,          // offset (bytes from start of buffer)
);

// This is not really needed but if we end up binding anything
// to ELEMENT_ARRAY_BUFFER, say we are generating indexed geometry
// we'll change cubeVertexArray's ELEMENT_ARRAY_BUFFER. By binding
// null here that won't happen.
gl.bindVertexArray(null);

// setup a transform feedback object to write to
// the position and color buffers
const tf = gl.createTransformFeedback();
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, positionBuffer);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, colorBuffer);
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);

// above this line is initialization code
// --------------------------------------
// below is rendering code.

// --------------------------------------
// First compute points into buffers

// no need to call the fragment shader
gl.enable(gl.RASTERIZER_DISCARD);

// unbind the buffers so we don't get errors.
gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, null);
gl.bindBuffer(gl.ARRAY_BUFFER, null);

gl.useProgram(genProg);

// generate numPoints of positions and colors
// into the buffers
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
gl.beginTransformFeedback(gl.POINTS);
gl.uniform1i(numPointsLoc, numPoints);
gl.drawArrays(gl.POINTS, 0, numPoints);
gl.endTransformFeedback();
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);

// turn on using fragment shaders again
gl.disable(gl.RASTERIZER_DISCARD);

// --------------------------------------
// Now draw using the buffers we just computed

gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

gl.bindVertexArray(dotVertexArray);
gl.useProgram(drawProg);
gl.drawArrays(gl.POINTS, 0, numPoints);
</script>
<script id="uniform-buffers" type="not-js">
'use strict';

const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl2');

const vsGLSL = `#version 300 es
uniform View {
  mat4 viewInverse;
  mat4 viewProjection;
};

uniform Light {
  highp vec3 lightWorldPos;
  mediump vec4 lightColor;
};

uniform Model {
  mat4 world;
  mat4 worldInverseTranspose;
} model;

in vec4 a_position;
in vec3 a_normal;

out vec4 v_position;
out vec3 v_normal;
out vec3 v_surfaceToLight;
out vec3 v_surfaceToView;

void main() {
  v_position = (viewProjection * model.world * a_position);
  v_normal = (model.worldInverseTranspose * vec4(a_normal, 0)).xyz;
  v_surfaceToLight = lightWorldPos - (model.world * a_position).xyz;
  v_surfaceToView = (viewInverse[3] - (model.world * a_position)).xyz;
  gl_Position = v_position;
}
`;

const fsGLSL = `#version 300 es
precision highp float;

in vec4 v_position;
in vec3 v_normal;
in vec3 v_surfaceToLight;
in vec3 v_surfaceToView;

uniform Light {
  highp vec3 lightWorldPos;
  mediump vec4 lightColor;
};

uniform Material {
  vec4 diffuse;
  vec4 ambient;
  vec4 specular;
  float shininess;
  float specularFactor;
};

out vec4 theColor;

void main() {
  vec3 a_normal = normalize(v_normal);
  vec3 surfaceToView = normalize(v_surfaceToView);
  vec3 surfaceToLight = normalize(v_surfaceToLight);
  vec3 halfVector = normalize(surfaceToLight + surfaceToView);
  float light = dot(a_normal, surfaceToLight) * 0.5 + 0.5;
  float specularLight = pow(max(0.0, dot(a_normal, halfVector)), shininess);
  theColor = vec4((
      diffuse * light +
      diffuse * ambient +
      specular * specularLight * specularFactor).rgb,
      diffuse.a);
}
`;

const createShader = function(gl, type, glsl) {
  const shader = gl.createShader(type)
  gl.shaderSource(shader, glsl)
  gl.compileShader(shader)
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    throw new Error(gl.getShaderInfoLog(shader))
  }
  return shader
};

const createProgram = function(gl, vsGLSL, fsGLSL) {
  const vs = createShader(gl, gl.VERTEX_SHADER, vsGLSL)
  const fs = createShader(gl, gl.FRAGMENT_SHADER, fsGLSL)
  const prg = gl.createProgram()
  gl.attachShader(prg, vs)
  gl.attachShader(prg, fs)
  gl.linkProgram(prg)
  if (!gl.getProgramParameter(prg, gl.LINK_STATUS)) {
    throw new Error(gl.getProgramInfoLog(prg))
  }

  // NOTE! These are only here to unclutter the diagram.
  // It is safe to detach and delete shaders once
  // a program is linked though it is arguably not common.
  // and I usually don't do it.
  gl.detachShader(prg, vs)
  gl.deleteShader(vs)
  gl.detachShader(prg, fs)
  gl.deleteShader(fs)

  return prg
};

const prg = createProgram(gl, vsGLSL, fsGLSL);

const positionLoc = gl.getAttribLocation(prg, 'a_position');
const normalLoc = gl.getAttribLocation(prg, 'a_normal');

// Look up the uniform blocks locations on the program and assign
// each one an index into the uniform buffer bind points
const viewUniformBufferIndex = 0;
const materialUniformBufferIndex = 1;
const modelUniformBufferIndex = 2;
const lightUniformBufferIndex = 3;
gl.uniformBlockBinding(
    prg, 
    gl.getUniformBlockIndex(prg, 'View'),
    viewUniformBufferIndex);
gl.uniformBlockBinding(
    prg, 
    gl.getUniformBlockIndex(prg, 'Model'),
    modelUniformBufferIndex);
gl.uniformBlockBinding(
    prg, 
    gl.getUniformBlockIndex(prg, 'Material'),
    materialUniformBufferIndex);
gl.uniformBlockBinding(
    prg, 
    gl.getUniformBlockIndex(prg, 'Light'),
    lightUniformBufferIndex);

// vertex positions for a cube
const cubeVertexPositions = new Float32Array([
    1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1,
]);
// vertex normals for a cube
const cubeVertexNormals = new Float32Array([
    1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1,
]);
// vertex indices for the triangles of a cube
// the data above defines 24 vertices. We need to draw 12
// triangles, 2 for each size, each triangle needs
// 3 vertices so 12 * 3 = 36
const cubeVertexIndices = new Uint16Array([
    0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23,
],);

const cubeVertexArray = gl.createVertexArray();
gl.bindVertexArray(cubeVertexArray);

const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, cubeVertexPositions, gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(
    positionLoc,  // location
    3,            // size (components per iteration)
    gl.FLOAT,     // type of to get from buffer
    false,        // normalize
    0,            // stride (bytes to advance each iteration)
    0,            // offset (bytes from start of buffer)
);

const normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, cubeVertexNormals, gl.STATIC_DRAW);
gl.enableVertexAttribArray(normalLoc);
gl.vertexAttribPointer(
    normalLoc,  // location
    3,          // size (components per iteration)
    gl.FLOAT,   // type of to get from buffer
    false,      // normalize
    0,          // stride (bytes to advance each iteration)
    0,          // offset (bytes from start of buffer)
);

const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndices, gl.STATIC_DRAW);


// This is not really needed but if we end up binding anything
// to ELEMENT_ARRAY_BUFFER, say we are generating indexed geometry
// we'll change cubeVertexArray's ELEMENT_ARRAY_BUFFER. By binding
// null here that won't happen.
gl.bindVertexArray(null);

// create a buffer for the view uniform block
// and an ArrayBuffer and 2 views into that buffer
// one for each uniform
const viewUniformBlockBuffer = gl.createBuffer();
const viewArrayBuffer = new ArrayBuffer(16 * 4 * 2);
const viewArrayData = new Float32Array(viewArrayBuffer);
const viewInverse = new Float32Array(viewArrayBuffer, 0, 16);
const viewProjection = new Float32Array(viewArrayBuffer, 16 * 4, 16);

// create a buffer for the model uniform block
// and an ArrayBuffer and 2 views into that buffer
// one for each uniform
const modelUniformBlockBuffer = gl.createBuffer();
const modelArrayBuffer = new ArrayBuffer(16 * 4 * 2);
const modelArrayData = new Float32Array(modelArrayBuffer);
const world = new Float32Array(modelArrayBuffer, 0, 16);
const worldInverseTranspose = new Float32Array(modelArrayBuffer, 16 * 4, 16);

// create a buffer for both the Light uniform block
const lightUniformBlockBuffer = gl.createBuffer();
const lightUniformBlockData = new Float32Array([
  0, 10, 30, 0,    // vec3, lightWorldPos, padded to 16 bytes
  1, 1, 1, 1,     // vec4, lightColor
]);
gl.bindBuffer(gl.UNIFORM_BUFFER, lightUniformBlockBuffer);
gl.bufferData(gl.UNIFORM_BUFFER, lightUniformBlockData, gl.STATIC_DRAW);

// create 3 buffers for 3 different material settings
const blueMatUniformBlockBuffer = gl.createBuffer();
gl.bindBuffer(gl.UNIFORM_BUFFER, blueMatUniformBlockBuffer);
gl.bufferData(gl.UNIFORM_BUFFER, new Float32Array([
    0.5, 0.7, 1, 1,  // vec4, diffuse
    0, 0, 0, 0,      // vec4, ambient
    1, 1, 1, 1,      // vec4, specular
    100,             // float, shininess
    1,               // float, specularFactor
    0, 0,            // padding
]), gl.STATIC_DRAW);

const redMatUniformBlockBuffer = gl.createBuffer();
gl.bindBuffer(gl.UNIFORM_BUFFER, redMatUniformBlockBuffer);
gl.bufferData(gl.UNIFORM_BUFFER, new Float32Array([
    1, 0.3, 0.5, 1,  // vec4, diffuse
    0, 0, 0, 0,      // vec4, ambient
    1, 1, 1, 1,      // vec4, specular
    50,              // float, shininess
    1,               // float, specularFactor
    0, 0,            // padding
]), gl.STATIC_DRAW);

const greenMatUniformBlockBuffer = gl.createBuffer();
gl.bindBuffer(gl.UNIFORM_BUFFER, greenMatUniformBlockBuffer);
gl.bufferData(gl.UNIFORM_BUFFER, new Float32Array([
    0.2, 1, 0.5, 1,  // vec4, diffuse
    0, 0, 0, 0,      // vec4, ambient
    1, 1, 1, 1,      // vec4, specular
    3,               // float, shininess
    1,               // float, specularFactor
    0, 0,            // padding
]), gl.STATIC_DRAW);

// above this line is initialization code
// --------------------------------------
// below is rendering code.

gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);

gl.useProgram(prg);

// compute the projection matrix into the TypedArray we declared above
m4.perspective(
  60 * Math.PI / 180,  // fov
  gl.canvas.clientWidth / gl.canvas.clientHeight,  // aspect
  0.1,  // near
  10,   // far
  viewProjection,
);
// compute the inverse view matrix (lazy so setting identity)
m4.identity(viewInverse);

// upload the view uniform data to the buffer.
gl.bindBuffer(gl.UNIFORM_BUFFER, viewUniformBlockBuffer);
gl.bufferData(gl.UNIFORM_BUFFER, viewArrayData, gl.DYNAMIC_DRAW);

// bind the view buffer to the uniform buffer bind point
// the program will look at
gl.bindBufferBase(
    gl.UNIFORM_BUFFER,
    viewUniformBufferIndex,
    viewUniformBlockBuffer);

// bind the model buffer to the uniform buffer bind point
// the program will look at
gl.bindBufferBase(
    gl.UNIFORM_BUFFER,
    modelUniformBufferIndex,
    modelUniformBlockBuffer);

// bind the light buffer to the uniform buffer bind point
// the program will look at
gl.bindBufferBase(
    gl.UNIFORM_BUFFER,
    lightUniformBufferIndex,
    lightUniformBlockBuffer);

gl.bindVertexArray(cubeVertexArray);

// compute the world matrices
m4.identity(world);
m4.translate(world, -2, 0, -4, world);
m4.xRotate(world, 0.5, world);
m4.yRotate(world, 0.5, world);
m4.inverse(world, worldInverseTranspose);
m4.transpose(worldInverseTranspose, worldInverseTranspose);

// upload the model uniform data to the buffer.
gl.bindBuffer(gl.UNIFORM_BUFFER, modelUniformBlockBuffer);
gl.bufferData(gl.UNIFORM_BUFFER, modelArrayData, gl.DYNAMIC_DRAW);

// bind the blue material buffer to the uniform buffer bind point
// the program will look at
gl.bindBufferBase(
    gl.UNIFORM_BUFFER,
    materialUniformBufferIndex,
    blueMatUniformBlockBuffer);

gl.drawElements(
    gl.TRIANGLES,
    36,                // num vertices to process
    gl.UNSIGNED_SHORT, // type of indices
    0,                 // offset on bytes to indices
);

// compute the world matrices
m4.identity(world);
m4.translate(world, 0, 0, -4, world);
m4.xRotate(world, 0.9, world);
m4.yRotate(world, 0.9, world);
m4.inverse(world, worldInverseTranspose);
m4.transpose(worldInverseTranspose, worldInverseTranspose);

// upload the model uniform data to the buffer.
gl.bindBuffer(gl.UNIFORM_BUFFER, modelUniformBlockBuffer);
gl.bufferData(gl.UNIFORM_BUFFER, modelArrayData, gl.DYNAMIC_DRAW);

// bind the red material buffer to the uniform buffer bind point
// the program will look at
gl.bindBufferBase(
    gl.UNIFORM_BUFFER,
    materialUniformBufferIndex,
    redMatUniformBlockBuffer);

gl.drawElements(
    gl.TRIANGLES,
    36,                // num vertices to process
    gl.UNSIGNED_SHORT, // type of indices
    0,                 // offset on bytes to indices
);

// compute the world matrices
m4.identity(world);
m4.translate(world, 3, 0, -4, world);
m4.xRotate(world, 0.7, world);
m4.yRotate(world, -0.6, world);
m4.inverse(world, worldInverseTranspose);
m4.transpose(worldInverseTranspose, worldInverseTranspose);

// upload the model uniform data to the buffer.
gl.bindBuffer(gl.UNIFORM_BUFFER, modelUniformBlockBuffer);
gl.bufferData(gl.UNIFORM_BUFFER, modelArrayData, gl.DYNAMIC_DRAW);

// bind the green material buffer to the uniform buffer bind point
// the program will look at
gl.bindBufferBase(
    gl.UNIFORM_BUFFER,
    materialUniformBufferIndex,
    greenMatUniformBlockBuffer);

gl.drawElements(
    gl.TRIANGLES,
    36,                // num vertices to process
    gl.UNSIGNED_SHORT, // type of indices
    0,                 // offset on bytes to indices
);
</script>
<script id="use-2-programs" type="not-js">
'use strict';

const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl2');

const vsGLSL1 = `#version 300 es
  in vec4 position;

  void main() {
    gl_Position = position;
    gl_PointSize = 10.0;
  }
`;

const fsGLSL1 = `#version 300 es
  precision highp float;
  uniform vec4 color;
  out vec4 outColor;
  void main() {
    outColor = color;
  }
`;

const vsGLSL2 = `#version 300 es
  in vec4 position;
  in vec4 color;
  out vec4 v_color;
  void main() {
    gl_Position = position;
    gl_PointSize = 10.0;
    v_color = color;
  }
`;

const fsGLSL2 = `#version 300 es
  precision highp float;
  in vec4 v_color;
  out vec4 outColor;
  void main() {
    outColor = v_color;
  }
`;

const createShader = function(gl, type, glsl) {
  const shader = gl.createShader(type)
  gl.shaderSource(shader, glsl)
  gl.compileShader(shader)
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    throw new Error(gl.getShaderInfoLog(shader))
  }
  return shader
};

const compileShadersAndLinkProgram = function(gl, prg, vsGLSL, fsGLSL) {
  const vertexShader = createShader(gl, gl.VERTEX_SHADER, vsGLSL)
  const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fsGLSL)
  gl.attachShader(prg, vertexShader)
  gl.attachShader(prg, fragmentShader)
  gl.linkProgram(prg)
  if (!gl.getProgramParameter(prg, gl.LINK_STATUS)) {
    throw new Error(gl.getProgramInfoLog(prg))
  }
  // NOTE! These are only here to unclutter the diagram.
  // It is safe to detach and delete shaders once
  // a program is linked though it is arguably not common.
  // and I usually don't do it.
  gl.detachShader(prg, vertexShader)
  gl.deleteShader(vertexShader)
  gl.detachShader(prg, fragmentShader)
  gl.deleteShader(fragmentShader)
  return prg
};

// program 1 uses 1 attribute and 1 uniform

const prog1 = gl.createProgram();
compileShadersAndLinkProgram(gl, prog1, vsGLSL1, fsGLSL1);
const prog1Locs = {
  position: gl.getAttribLocation(prog1, 'position'),
  color: gl.getUniformLocation(prog1, 'color'),
};

// program 2 uses 2 attributes and no uniforms
const prog2 = gl.createProgram();
compileShadersAndLinkProgram(gl, prog2, vsGLSL2, fsGLSL2);
const prog2Locs = {
  position: gl.getAttribLocation(prog2, 'position'),
  color: gl.getAttribLocation(prog2, 'color'),
};

// vertex positions for a  ⃟
const prog1VertexPositions = new Float32Array([
   -0.2,  0,
   -0.8,  0,
   -0.3, -0.2,
   -0.7, -0.2,
   -0.3,  0.2,
   -0.7,  0.2,
   -0.4,  0.4,
   -0.6,  0.4,
   -0.4, -0.4,
   -0.6, -0.4,
   -0.5,  0.6,
   -0.5, -0.6,
]);

// vertex positions for a +
const prog2VertexPositions = new Float32Array([
   0.5,  0,
   0.5, -0.2,
   0.5,  0.2,
   0.5, -0.4,
   0.5,  0.4,
   0.4,  0,
   0.6,  0,
   0.3,  0,
   0.7,  0,
]);

const prog2VertexColors = new Float32Array([
   1, 0, 0,  // red
   0, 0, 1,  // blue
   0, 0, 1,
   0, 1, 0,  // green
   0, 1, 0,
   0, 1, 1,  // cyan
   0, 1, 1,
   1, 0, 1,  // magenta
   1, 0, 1,
]);

const position1Buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, position1Buffer);
gl.bufferData(gl.ARRAY_BUFFER, prog1VertexPositions, gl.STATIC_DRAW);

const position2Buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, position2Buffer);
gl.bufferData(gl.ARRAY_BUFFER, prog2VertexPositions, gl.STATIC_DRAW);

const color2Buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, color2Buffer);
gl.bufferData(gl.ARRAY_BUFFER, prog2VertexColors, gl.STATIC_DRAW);

// this is not needed. It's just here to unclutter the diagram
gl.bindBuffer(gl.ARRAY_BUFFER, null);

// above this line is initialization code
// --------------------------------------
// below is rendering code.

// --------------------------------------
// First draw a diamond of points with the first program

// set the attributes to what's needed for program 1
gl.bindBuffer(gl.ARRAY_BUFFER, position1Buffer);
gl.enableVertexAttribArray(prog1Locs.position);
gl.vertexAttribPointer(
    prog1Locs.position, // location
    2,                  // size (components per iteration)
    gl.FLOAT,           // type of to get from buffer
    false,              // normalize
    0,                  // stride (bytes to advance each iteration)
    0,                  // offset (bytes from start of buffer)
);

// this is not needed. It's just here to unclutter the diagram
gl.bindBuffer(gl.ARRAY_BUFFER, null);

gl.useProgram(prog1);

// orange
gl.uniform4fv(prog1Locs.color, [1, 0.7, 0.5, 1]);

// draw 12 points
gl.drawArrays(gl.POINTS, 0, 12);


// --------------------------------------
// Second, draw a plus of points with the second program

// set the attributes to what's needed for program 1
gl.bindBuffer(gl.ARRAY_BUFFER, position2Buffer);
gl.enableVertexAttribArray(prog2Locs.position);
gl.vertexAttribPointer(
    prog2Locs.position, // location
    2,                  // size (components per iteration)
    gl.FLOAT,           // type of to get from buffer
    false,              // normalize
    0,                  // stride (bytes to advance each iteration)
    0,                  // offset (bytes from start of buffer)
);

gl.bindBuffer(gl.ARRAY_BUFFER, color2Buffer);
gl.enableVertexAttribArray(prog2Locs.color);
gl.vertexAttribPointer(
    prog2Locs.color,    // location
    3,                  // size (components per iteration)
    gl.FLOAT,           // type of to get from buffer
    false,              // normalize
    0,                  // stride (bytes to advance each iteration)
    0,                  // offset (bytes from start of buffer)
);

// this is not needed. It's just here to unclutter the diagram
gl.bindBuffer(gl.ARRAY_BUFFER, null);

gl.useProgram(prog2);

// draw 9 points
gl.drawArrays(gl.POINTS, 0, 9);
</script>
<script id="disabled-vertex-attrib" type="not-js">
'use strict';

const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl2');

const vsGLSL = `#version 300 es
  in vec4 position;
  in vec4 color;

  uniform vec4 offset;

  out vec4 v_color;

  void main() {
    gl_Position = position + offset;
    v_color = color;
  }
`;

const fsGLSL = `#version 300 es
  precision highp float;
  in vec4 v_color;
  out vec4 fragColor;
  void main() {
    fragColor = v_color;
  }
`;

const createShader = function(gl, type, glsl) {
  const shader = gl.createShader(type)
  gl.shaderSource(shader, glsl)
  gl.compileShader(shader)
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    throw new Error(gl.getShaderInfoLog(shader))
  }
  return shader
};

const compileShadersAndLinkProgram = function(gl, prg, vsGLSL, fsGLSL) {
  const vertexShader = createShader(gl, gl.VERTEX_SHADER, vsGLSL)
  const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fsGLSL)
  gl.attachShader(prg, vertexShader)
  gl.attachShader(prg, fragmentShader)
  gl.linkProgram(prg)
  if (!gl.getProgramParameter(prg, gl.LINK_STATUS)) {
    throw new Error(gl.getProgramInfoLog(prg))
  }
  // NOTE! These are only here to unclutter the diagram.
  // It is safe to detach and delete shaders once
  // a program is linked though it is arguably not common.
  // and I usually don't do it.
  gl.detachShader(prg, vertexShader)
  gl.deleteShader(vertexShader)
  gl.detachShader(prg, fragmentShader)
  gl.deleteShader(fragmentShader)
  return prg
};

const prog = gl.createProgram();
compileShadersAndLinkProgram(gl, prog, vsGLSL, fsGLSL);
const positionLoc = gl.getAttribLocation(prog, 'position');
const colorLoc = gl.getAttribLocation(prog, 'color');
const offsetLoc = gl.getUniformLocation(prog, 'offset');

// vertex positions for triangle
const vertexPositions = new Float32Array([
   0.0,  0.4,
  -0.4, -0.4,
   0.4, -0.4,
]);

const vertexColors = new Float32Array([
  1, 1, 0, 1,  // yellow
  0, 1, 1, 1,  // cyan
  1, 0, 1, 1,  // magenta
]);

const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertexPositions, gl.STATIC_DRAW);

const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertexColors, gl.STATIC_DRAW);

// this is not needed. It's just here to unclutter the diagram
gl.bindBuffer(gl.ARRAY_BUFFER, null);

// above this line is initialization code
// --------------------------------------
// below is rendering code.

// --------------------------------------
// First draw a triangle with a different color for each vertex

// set the attributes
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(
    positionLoc, // location
    2,           // size (components per iteration)
    gl.FLOAT,    // type of to get from buffer
    false,       // normalize
    0,           // stride (bytes to advance each iteration)
    0,           // offset (bytes from start of buffer)
);

gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.enableVertexAttribArray(colorLoc);
gl.vertexAttribPointer(
    colorLoc, // location
    4,        // size (components per iteration)
    gl.FLOAT, // type of to get from buffer
    false,    // normalize
    0,        // stride (bytes to advance each iteration)
    0,        // offset (bytes from start of buffer)
);

// this is not needed. It's just here to unclutter the diagram
gl.bindBuffer(gl.ARRAY_BUFFER, null);

gl.useProgram(prog);

// draw on left
gl.uniform4fv(offsetLoc, [-0.5, 0, 0, 0]);

// draw 3 vertices (1 triangle)
gl.drawArrays(gl.TRIANGLES, 0, 3);

// Now draw the triangle again in a solid color 
// by turning off the color attribute and setting
// an attribute values
gl.disableVertexAttribArray(colorLoc);
gl.vertexAttrib4fv(colorLoc, [0.3, 0.6, 0.9, 1]);

// draw on right
gl.uniform4fv(offsetLoc, [0.5, 0, 0, 0]);

// draw 3 vertices (1 triangle)
gl.drawArrays(gl.TRIANGLES, 0, 3);
</script>
<script src="/webgl/resources/chroma.min.js"></script>
<script src="/3rdparty/showdown.min.js"></script>
<script src="/3rdparty/prism.js" data-manual></script>
<script src="../../resources/m4.js"></script>
<script src="webgl-state-diagram/eval-helper.js"></script>
<script type="module">
import webglStateDiagram from './webgl-state-diagram/state-diagram.js';
webglStateDiagram({
  webglVersion: 'webgl2',
  examples: {
    'draw-cubes': {
      name: 'Draw Cubes',
      windowPositions: [
        { note: 'canvas',       base: '#diagram',       x: 'right:right-10', y: 'top:top+10', },
        { note: 'transform-feedback', base: '#diagram',       x: 'left:left+30',   y: 'bottom:bottom-30', },
        { note: 'vertex-array', base: '#diagram',       x: 'left:left+50',   y: 'bottom:bottom-50', },
        { note: 'global-state', base: '#diagram',       x: 'left:left+10',   y: 'top:top+10', },
        { note: 'v-shader',     base: 'canvas',         x: 'left:left-50',   y: 'top:bottom+10', },
        { note: 'f-shader',     base: 'vertexShader',   x: 'left:left-10',   y: 'top:bottom-90', },
        { note: 'program',      base: 'global state',   x: 'left:right+60',  y: 'top:top+0', },
        { note: 'c-vert-array', base: 'vertex array',   x: 'left:left-30',   y: 'top:top+30', },
        { note: 'p-buffer',     base: 'canvas',         x: 'left:left+70',   y: 'top:bottom+10', },
        { note: 'n-buffer',     base: 'positionBuffer', x: 'left:left-0',    y: 'top:bottom+10', },
        { note: 't-buffer',     base: 'normalBuffer',   x: 'left:left-0',    y: 'top:bottom+10', },
        { note: 'i-buffer',     base: 'texcoordBuffer', x: 'left:left-0',    y: 'top:bottom+10', },
        { note: 'ch-texture ',  base: 'indexBuffer',    x: 'left:left-0',    y: 'top:bottom+10', },
        { note: 'dc-texture ',  base: 'checkerTexture', x: 'left:left-20',   y: 'top:top+20', },
      ],
    },
    'draw-cube-on-cube': {
      name: 'Draw Cube on Cube',
      windowPositions: [
        { note: 'canvas',       base: '#diagram',       x: 'right:right-10', y: 'top:top+10', },
        { note: 'transform-feedback', base: '#diagram',       x: 'left:left+30',   y: 'bottom:bottom-30', },
        { note: 'vertex-array', base: '#diagram',       x: 'left:left+50',   y: 'bottom:bottom-50', },
        { note: 'global-state', base: '#diagram',       x: 'left:left+10',   y: 'top:top+10', },
        { note: 'v-shader',     base: 'canvas',         x: 'left:left-50',   y: 'top:bottom+10', },
        { note: 'f-shader',     base: 'vertexShader',   x: 'left:left-10',   y: 'top:bottom-90', },
        { note: 'program',      base: 'global state',   x: 'left:right+60',  y: 'top:top+0', },
        { note: 'c-vert-array', base: 'vertex array',   x: 'left:left-30',   y: 'top:top+30', },
        { note: 'p-buffer',     base: 'canvas',         x: 'left:left+70',   y: 'top:bottom+10', },
        { note: 'n-buffer',     base: 'positionBuffer', x: 'left:left-0',    y: 'top:bottom+10', },
        { note: 't-buffer',     base: 'normalBuffer',   x: 'left:left-0',    y: 'top:bottom+10', },
        { note: 'i-buffer',     base: 'texcoordBuffer', x: 'left:left-0',    y: 'top:bottom+10', },
        { note: 'ch-texture',   base: 'indexBuffer',    x: 'left:left-0',    y: 'top:bottom+10', },
        { note: 'fb-texture',   base: 'checkerTexture', x: 'left:left-20',   y: 'top:top+20', },
        { note: 'renderbuffer', base: 'texcoordBuffer', x: 'right:left+100', y: 'top:top+40', },
        { note: 'framebuffer',  base: 'program',        x: 'left:left+0',    y: 'top:bottom+30', },
      ],
    },
    'smallest': {
      name: 'Smallest',
      windowPositions: [
        { note: 'canvas',       base: '#diagram',       x: 'right:right-10', y: 'top:top+10', },
        { note: 'transform-feedback', base: '#diagram',       x: 'left:left+30',   y: 'bottom:bottom-30', },
        { note: 'vertex-array', base: '#diagram',       x: 'left:left+50',   y: 'bottom:bottom-50', },
        { note: 'global-state', base: '#diagram',       x: 'left:left+10',   y: 'top:top+10', },
      ],
    },
    'smallest-glsl': {
      name: 'Smallest GLSL',
      windowPositions: [
        { note: 'canvas',       base: '#diagram',       x: 'right:right-10', y: 'top:top+10', },
        { note: 'transform-feedback', base: '#diagram',       x: 'left:left+30',   y: 'bottom:bottom-30', },
        { note: 'vertex-array', base: '#diagram',       x: 'left:left+50',   y: 'bottom:bottom-50', },
        { note: 'global-state', base: '#diagram',       x: 'left:left+10',   y: 'top:top+10', },
        { note: 'v-shader',     base: 'canvas',         x: 'left:left-50',   y: 'top:bottom+10', },
        { note: 'f-shader',     base: 'vertexShader',   x: 'left:left-10',   y: 'top:bottom+10', },
        { note: 'program',      base: 'global state',   x: 'left:right+60',  y: 'top:top+0', },
      ],
    },
    'triangle': {
      name: 'Triangle',
      windowPositions: [
        { note: 'canvas',       base: '#diagram',       x: 'right:right-10', y: 'top:top+10', },
        { note: 'transform-feedback', base: '#diagram',       x: 'left:left+30',   y: 'bottom:bottom-30', },
        { note: 'vertex-array', base: '#diagram',       x: 'left:left+50',   y: 'bottom:bottom-50', },
        { note: 'global-state', base: '#diagram',       x: 'left:left+10',   y: 'top:top+10', },
        { note: 'v-shader',     base: 'canvas',         x: 'left:left-50',   y: 'top:bottom+10', },
        { note: 'f-shader',     base: 'vertexShader',   x: 'left:left-10',   y: 'top:bottom+10', },
        { note: 'program',      base: 'global state',   x: 'left:right+60',  y: 'top:top+0', },
        { note: 'c-vert-array', base: 'vertex array',   x: 'left:left-30',   y: 'top:top+30', },
        { note: 'p-buffer',     base: 'canvas',         x: 'left:left+70',   y: 'top:bottom+10', },
      ],
    },
    'rainbow-triangle': {
      name: 'Rainbow Triangle',
      windowPositions: [
        { note: 'canvas',       base: '#diagram',       x: 'right:right-10', y: 'top:top+10', },
        { note: 'transform-feedback', base: '#diagram',       x: 'left:left+30',   y: 'bottom:bottom-30', },
        { note: 'vertex-array', base: '#diagram',       x: 'left:left+50',   y: 'bottom:bottom-50', },
        { note: 'global-state', base: '#diagram',       x: 'left:left+10',   y: 'top:top+10', },
        { note: 'v-shader',     base: 'canvas',         x: 'left:left-50',   y: 'top:bottom+10', },
        { note: 'f-shader',     base: 'vertexShader',   x: 'left:left-10',   y: 'top:bottom+10', },
        { note: 'program',      base: 'global state',   x: 'left:right+60',  y: 'top:top+0', },
        { note: 'c-vert-array', base: 'vertex array',   x: 'left:left-30',   y: 'top:top+30', },
        { note: 'p-buffer',     base: 'canvas',         x: 'left:left+70',   y: 'top:bottom+10', },
        { note: 'c-buffer',     base: 'positionBuffer', x: 'left:left-0',    y: 'top:bottom+10', },
      ],
    },
    'use-2-programs': {
      name: 'Use 2 Programs',
      windowPositions: [
        { note: 'canvas',       base: '#diagram',       x: 'right:right-10', y: 'top:top+10', },
        { note: 'transform-feedback', base: '#diagram',       x: 'left:left+30',   y: 'bottom:bottom-30', },
        { note: 'vertex-array', base: '#diagram',       x: 'left:left+10',   y: 'bottom:bottom-10', },
        { note: 'global-state', base: '#diagram',       x: 'left:left+10',   y: 'top:top+10', },
        { note: 'program',      base: 'global state',   x: 'left:right+60',  y: 'top:top+0', },
        { note: 'v-shader',     base: 'canvas',         x: 'left:left-50',   y: 'top:bottom+10', },
        { note: 'f-shader',     base: 'vertexShader',   x: 'left:left-10',   y: 'top:bottom-90', },
        { note: 'program',      base: 'prog1',          x: 'left:left+10',   y: 'top:bottom+10', },
        { note: 'v-shader',     base: 'canvas',         x: 'left:left-50',   y: 'top:bottom+10', },
        { note: 'f-shader',     base: 'vertexShader',   x: 'left:left-10',   y: 'top:bottom-90', },
        { note: 'p1-buffer',    base: 'canvas',         x: 'left:left+70',   y: 'top:bottom+10', },
        { note: 'p2-buffer',    base: 'position1Buffer',x: 'left:left+0',    y: 'top:bottom+10', },
        { note: 'c-buffer',     base: 'position2Buffer',x: 'left:left-0',    y: 'top:bottom+10', },
      ],
    },
    'samplers': {
      name: 'Samplers',
      windowPositions: [
        { note: 'canvas',       base: '#diagram',       x: 'right:right-10', y: 'top:top+10', },
        { note: 'transform-feedback', base: '#diagram',       x: 'left:left+30',   y: 'bottom:bottom-30', },
        { note: 'vertex-array', base: '#diagram',       x: 'left:left+50',   y: 'bottom:bottom-50', },
        { note: 'global-state', base: '#diagram',       x: 'left:left+10',   y: 'top:top+10', },
        { note: 'v-shader',     base: 'canvas',         x: 'left:left-50',   y: 'top:bottom+10', },
        { note: 'f-shader',     base: 'vertexShader',   x: 'left:left-10',   y: 'top:bottom-90', },
        { note: 'program',      base: 'global state',   x: 'left:right+110', y: 'top:top+0', },
        { note: 'c-vert-array', base: 'vertex array',   x: 'left:left-30',   y: 'top:top+30', },
        { note: 'p-buffer',     base: 'canvas',         x: 'left:left+70',   y: 'top:bottom+10', },
        { note: 'n-buffer',     base: 'positionBuffer', x: 'left:left-0',    y: 'top:bottom+10', },
        { note: 't-buffer',     base: 'normalBuffer',   x: 'left:left-0',    y: 'top:bottom+10', },
        { note: 'i-buffer',     base: 'texcoordBuffer', x: 'left:left-0',    y: 'top:bottom+10', },
        { note: 'ch-texture',   base: 'indexBuffer',    x: 'left:left-0',    y: 'top:bottom+10', },
        { note: 'lin-sampler',  base: 'checkerTexture', x: 'left:left-120',  y: 'top:top-50', },
        { note: 'near-sampler', base: 'linearSampler',  x: 'left:left+20',   y: 'top:top+50', },
      ],
    },
    'transform-feedback': {
      name: 'Transform Feedback',
      adjust: [
        {cmd: 'expand',   id: 'globalUI.transformFeedbackState'},
        {cmd: 'collapse', id: 'globalUI.depthState'},
      ],
      windowPositions: [
        { note: 'canvas',       base: '#diagram',       x: 'right:right-10', y: 'top:top+10', },
        { note: 'transform-feedback', base: '#diagram',       x: 'left:left+30',   y: 'bottom:bottom-30', },
        { note: 'vertex-array', base: '#diagram',       x: 'left:left+50',   y: 'bottom:bottom-50', },
        { note: 'global-state', base: '#diagram',       x: 'left:left+10',   y: 'top:top+10', },
          { note: 'vs1',          base: '#diagram',       x: 'left:left+0',    y: 'top:top+0', },
          { note: 'fs1',          base: '#diagram',       x: 'left:left+0',    y: 'top:top+0', },
        { note: 'gen-program',  base: 'global state',   x: 'left:right+110', y: 'top:top+0', },
          { note: 'vs2',          base: '#diagram',       x: 'left:left+0',    y: 'top:top+0', },
          { note: 'fs2',          base: '#diagram',       x: 'left:left+0',    y: 'top:top+0', },
        { note: 'draw-program', base: 'genProg',        x: 'left:left+0',    y: 'top:bottom+10', },
        { note: 'c-vert-array', base: 'vertex array',   x: 'left:left-30',   y: 'top:top+30', },
        { note: 'p-buffer',     base: 'canvas',         x: 'left:left+70',   y: 'top:bottom+10', },
        { note: 'c-buffer',     base: 'positionBuffer', x: 'left:left-0',    y: 'top:bottom+10', },
        { note: 'tr-feedback',  base: 'colorBuffer',    x: 'left:left-50',   y: 'top:bottom+10', },
      ],
    },
    'uniform-buffers': {
      name: 'Uniform Buffers',
      adjust: [
        {cmd: 'expand',   id: 'globalUI.uniformBufferBindingsState'},
        {cmd: 'collapse', id: 'globalUI.depthState'},
        {cmd: 'collapse', id: 'globalUI.textureUnits'},
      ],
      windowPositions: [
        { note: 'canvas',       base: '#diagram',       x: 'right:right-10', y: 'top:top+10', },
        { note: 'transform-feedback', base: '#diagram',       x: 'left:left+30',   y: 'bottom:bottom-30', },
        { note: 'vertex-array', base: '#diagram',       x: 'left:left+50',   y: 'bottom:bottom-50', },
        { note: 'global-state', base: '#diagram',       x: 'left:left+10',   y: 'top:top+10', },
          { note: 'vs1',          base: '#diagram',       x: 'left:left+0',    y: 'top:top+0', },
          { note: 'fs1',          base: '#diagram',       x: 'left:left+0',    y: 'top:top+0', },
        { note: 'draw-program',  base: 'global state',  x: 'left:right+110', y: 'top:top+0', },
        { note: 'c-vert-array', base: 'vertex array',   x: 'left:left-30',   y: 'top:top+30', },
        { note: 'p-buffer',     base: 'canvas',            x: 'left:left+70',   y: 'top:bottom+10', },
        { note: 'n-buffer',     base: 'positionBuffer',            x: 'left:left-3',    y: 'top:bottom-10', },
        { note: 'i-buffer',     base: 'normalBuffer',              x: 'left:left-3',    y: 'top:bottom-10', },
        { note: 'v-buffer',     base: 'indexBuffer',               x: 'left:left-3',    y: 'top:bottom-10', },
        { note: 'm-buffer',     base: 'viewUniformBlockBuffer',    x: 'left:left-3',    y: 'top:bottom+10', },
        { note: 'l-buffer',     base: 'modelUniformBlockBuffer',   x: 'left:left-3',    y: 'top:bottom+10', },
        { note: 'bm-buffer',    base: 'lightUniformBlockBuffer',   x: 'left:left-3',    y: 'top:bottom-10', },
        { note: 'rm-buffer',    base: 'blueMatUniformBlockBuffer', x: 'left:left-3',    y: 'top:bottom-10', },
        { note: 'gm-buffer',    base: 'redMatUniformBlockBuffer',  x: 'left:left-3',    y: 'top:bottom-10', },
      ],
    },
    'disabled-vertex-attrib': {
      name: 'Disabled Vertex Attrib',
      adjust: [
        {cmd: 'expand',   id: 'globalUI.attribValueState'},
      ],
      windowPositions: [
        { note: 'canvas',       base: '#diagram',       x: 'right:right-10', y: 'top:top+10', },
        { note: 'transform-feedback', base: '#diagram',       x: 'left:left+30',   y: 'bottom:bottom-30', },
        { note: 'vertex-array', base: '#diagram',       x: 'left:left+460',   y: 'bottom:bottom-10', },
        { note: 'global-state', base: '#diagram',       x: 'left:left+10',   y: 'top:top+10', },
        { note: 'program',      base: 'global state',   x: 'left:right+60',  y: 'top:top+0', },
        { note: 'v-shader',     base: 'canvas',         x: 'left:left-50',   y: 'top:bottom+10', },
        { note: 'f-shader',     base: 'vertexShader',   x: 'left:left-10',   y: 'top:bottom-90', },
        { note: 'p-buffer',     base: 'canvas',         x: 'left:left+70',   y: 'top:bottom+10', },
        { note: 'c-buffer',     base: 'positionBuffer', x: 'left:left-0',    y: 'top:bottom+10', },
      ],
    },
  },
});
</script>
</html>
